/**********************************************************************
 * File:        arab_classes.cpp
 * Description: Inherited classes with more attributes for arabic writings.
 * Author:      Fuad Jamour
 * Created:     Tue Mar 10 2009
 **********************************************************************/

#include		"mfcpch.h"     //precompiled headers
#include		"arab_classes.h"
#include		"tprintf.h"
#include		"varable.h"
#include        "ccmain\tessvars.h"
#include		"statistc.h"
#include		<string>

//#define		WRITE_IMAGE_WITH_BOXES
#define		WRITE_IMAGE_WDETAILS




ELIST2IZE (PixelWithFeatures)	ELIST2IZE (PixelWithDiffs)	ELIST2IZE (Route)	ELIST2IZE (PolygonalEdge)	ELIST2IZE (SEG_POINT)



//Segmentation parameters
#define		MAX_ROUTE_SLOPE_TO_SEGMENT					1.0
#define		AVG_STROKE_WIDTH							6
#define		MIN_ROUTE_LENGTH_TO_SEGMENT					2*AVG_STROKE_WIDTH
#define		MIN_ROUTE_LENGTH_BR_BR						5
#define		HALF_WINDOW_WIDTH_SUBTLE_BRANCH				20
#define		MIN_Y_DIFF_TO_CONSIDER_SUBTLE_BRNACH		1
#define		MAX_Y_DIFF_TO_CONSIDER_SUBTLE_BRNACH		HALF_WINDOW_WIDTH_SUBTLE_BRANCH
#define		POLYGONAL_MAX_DIST_TO_STOP					1.75
#define		MAX_POLY_EDGE_ANGLE_TO_KEEP					2.8
#define		MIN_ANGLE_LOC_MIN							1.75

/**********************************************************************
 * makeThinnedImage
 *
 * Creates a thinned version of the image. If the flag keepOrigText is true, the thinned image will be 
 * drawn inside the original image, the original image will have the value 0 and the thinned image 
 * will have the value thinnedGreyLevel
 **********************************************************************/
 void ARAB_IMAGE::makeThinnedImage(bool keepOrigText) {
	 thinned = new IMAGE();
	 thinned->create(this->get_xsize(), this->get_ysize(), this->get_bpp());
	 copy_sub_image(this, 0, 0, this->get_xsize(), this->get_ysize(), thinned, 0, 0, 0);//Copying the original image
	 //mysource->write("step4.tif");
	 IMAGELINE line, line2;
	 line.init();
	 line2.init();

	 IMAGE  temp_img, mysource;

	 //Ahmad Sep 17, 2008
	 mysource.create(this->get_xsize(), this->get_ysize(), 1);
	 copy_sub_image(this,0,0,this->get_xsize(),this->get_ysize(),&mysource ,0,0,1);
	 //Ahmad end
	 temp_img.create(mysource.get_xsize(), mysource.get_ysize(), mysource.get_bpp());
	 copy_sub_image(&mysource,0,0,mysource.get_xsize(),mysource.get_ysize(),&temp_img ,0,0,0);

	 /*this->write("this.tif");
	 mysource.write("mysource.tif");
	 temp_img.write("temp.tif");
	 */
	 int wctr = 0;
	 int hctr = 0;
	 int wctrUB = mysource.get_xsize();
	 int hctrUB = mysource.get_ysize();
	 bool DFlag = FALSE;
	 int NT, NZ, PrevPoint;
	 int x[9];

	 //this loop clears temp_img
	 for (int j = hctrUB-1; j >=0 ; --j)
	 {
		 temp_img.get_line(0, j, wctrUB, &line, 0);

		 for(int s=0;s<=wctrUB;s++)
			 line.pixels[s]=0;

		 temp_img.put_line(0, j, wctrUB, &line, 0);
	 }

	 //this loop reverses the colors of the image 
	 for (int j = hctrUB-1; j >=0 ; --j)
	 {
		 mysource.get_line(0, j, wctrUB, &line, 0);

		 for(int s=0;s<=wctrUB;s++)
		 {
			 if(line.pixels[s]==0)
				 line.pixels[s]=1;
			 else
				 line.pixels[s]=0;
		 }

		 mysource.put_line(0, j, wctrUB, &line, 0);
	 }

	 while(DFlag == FALSE)			
	 {
		 DFlag = TRUE;
		 wctrUB = mysource.get_xsize();
		 hctrUB = mysource.get_ysize();

		 // Iteration 1 ------------------------------
		 for(hctr = hctrUB - 2; hctr > 0; hctr--)
		 {
			 for(wctr = wctrUB - 2; wctr > 0 ; wctr--)
			 {
				 if(mysource.pixel(wctr,hctr))
				 {
					 /*x[1] = mysource.pixel(wctr+1,hctr);
					 x[2] = mysource.pixel(wctr+1,hctr-1);
					 x[3] = mysource.pixel( wctr ,hctr-1);
					 x[4] = mysource.pixel(wctr-1,hctr-1);
					 x[5] = mysource.pixel(wctr-1,hctr);
					 x[6] = mysource.pixel(wctr-1,hctr+1);
					 x[7] = mysource.pixel( wctr ,hctr+1);
					 x[8] = mysource.pixel(wctr+1,hctr+1);*/
					 x[1] = mysource.pixel(wctr+1,hctr);
					 x[8] = mysource.pixel(wctr+1,hctr-1);
					 x[7] = mysource.pixel( wctr ,hctr-1);
					 x[6] = mysource.pixel(wctr-1,hctr-1);
					 x[5] = mysource.pixel(wctr-1,hctr);
					 x[4] = mysource.pixel(wctr-1,hctr+1);
					 x[3] = mysource.pixel( wctr ,hctr+1);
					 x[2] = mysource.pixel(wctr+1,hctr+1);

					 // find number of black neighbors
					 NZ = 0;
					 for (int i=1; i<9; i++)
						 if (x[i])
							 NZ++;

					 // find number of 01 transitions
					 NT = 0;
					 PrevPoint = x[8];
					 for (int i=1; i<9; i++) 
					 {
						 if (PrevPoint==0 && x[i]!=0)
							 NT++;
						 PrevPoint = x[i];
					 }

					 if ((NT <= 2) &&
						 (NZ !=1) &&
						 ((x[1] & x[3] & x[5])==0) &&
						 ((x[1] & x[3] & x[7])==0) && (NZ !=0))
					 {
						 if (NT != 2) 
						 {
							 temp_img.get_line(wctr, hctr, 1, &line, 0);
							 line.pixels[0]=1;
							 temp_img.put_line(wctr, hctr, 1, &line, 0);
							 DFlag = FALSE;
						 }
						 else 
							 if ((((x[1]&x[7])==1) && ((x[2]|x[6])==1) &&
								 ((x[3]|x[4]|x[5]|x[8])==0)) ||
								 (((x[1]&x[3])==1) && ((x[4]|x[8])==1) &&
								 ((x[2]|x[5]|x[6]|x[7])==0))) 
							 {
								 temp_img.get_line(wctr, hctr, 1, &line, 0);
								 line.pixels[0]=1;
								 temp_img.put_line(wctr, hctr, 1, &line, 0);
								 DFlag = FALSE;
							 }
					 }
				 } // end if
			 }  // end for
		 }  // end for

		 for(hctr = hctrUB - 2; hctr > 0; hctr--)
			 for(wctr = wctrUB - 2; wctr > 0 ; wctr--)
				 if(temp_img.pixel(wctr,hctr) == 1)
				 {
					 mysource.get_line(wctr, hctr, 1, &line, 0);
					 line.pixels[0]=0;
					 mysource.put_line(wctr, hctr, 1, &line, 0);
				 }

				 // Iteration 2 ------------------------------
				 for(hctr = hctrUB - 2; hctr > 0; hctr--)
				 {
					 for(wctr = wctrUB - 2; wctr > 0 ; wctr--)
					 {
						 if(mysource.pixel(wctr,hctr))
						 {
							 /*x[1] = mysource.pixel(wctr+1,hctr);
							 x[2] = mysource.pixel(wctr+1,hctr-1);
							 x[3] = mysource.pixel( wctr ,hctr-1);
							 x[4] = mysource.pixel(wctr-1,hctr-1);
							 x[5] = mysource.pixel(wctr-1,hctr);
							 x[6] = mysource.pixel(wctr-1,hctr+1);
							 x[7] = mysource.pixel( wctr ,hctr+1);
							 x[8] = mysource.pixel(wctr+1,hctr+1);*/
							 x[1] = mysource.pixel(wctr+1,hctr);
							 x[8] = mysource.pixel(wctr+1,hctr-1);
							 x[7] = mysource.pixel( wctr ,hctr-1);
							 x[6] = mysource.pixel(wctr-1,hctr-1);
							 x[5] = mysource.pixel(wctr-1,hctr);
							 x[4] = mysource.pixel(wctr-1,hctr+1);
							 x[3] = mysource.pixel( wctr ,hctr+1);
							 x[2] = mysource.pixel(wctr+1,hctr+1);

							 // find number of black neighbors
							 NZ = 0;
							 for (int i=1; i<9; i++)
								 if (x[i])
									 NZ++;

							 // find number of 01 transitions
							 NT = 0;
							 PrevPoint = x[8];
							 for (int i=1; i<9; i++) 
							 {
								 if (PrevPoint==0 && x[i]!=0)
									 NT++;
								 PrevPoint = x[i];
							 }

							 if ((NT <= 2) &&
								 (NZ !=1) &&
								 ((x[3] & x[5] & x[7])==0) &&
								 ((x[5] & x[7] & x[1])==0) && (NZ !=0)) 
							 {
								 if (NT != 2) 
								 {
									 temp_img.get_line(wctr, hctr, 1, &line, 0);
									 line.pixels[0]=1;
									 temp_img.put_line(wctr, hctr, 1, &line, 0);
									 DFlag = FALSE;
								 }
								 else if ((((x[5]&x[3])==1) && ((x[6]|x[2])==1) &&
									 ((x[1]|x[4]|x[7]|x[8])==0)) ||
									 (((x[7]&x[5])==1) && ((x[8]|x[4])==1) &&
									 ((x[1]|x[2]|x[3]|x[6])==0))) 
								 { //5672
									 temp_img.get_line(wctr, hctr, 1, &line, 0);
									 line.pixels[0]=1;
									 temp_img.put_line(wctr, hctr, 1, &line, 0);
									 DFlag = FALSE;
								 }
							 }
						 } // end if
					 }  // end for
				 }  // end for

				 for(hctr = hctrUB - 2; hctr > 0; hctr--)
					 for(wctr = wctrUB - 2; wctr > 0 ; wctr--)
						 if(temp_img.pixel(wctr,hctr) == 1)
						 {
							 mysource.get_line(wctr, hctr, 1, &line, 0);
							 line.pixels[0]=0;
							 mysource.put_line(wctr, hctr, 1, &line, 0);
						 }
	 }

	 // begin stair case removal
	 int index;
	 for(hctr = hctrUB - 2; hctr > 0; hctr--)
	 {
		 for(wctr = wctrUB - 2; wctr > 0; wctr--)
			{
				if(mysource.pixel(wctr,hctr))
				{
					/*x[1] = mysource.pixel(wctr+1,hctr);
					x[2] = mysource.pixel(wctr+1,hctr-1);
					x[3] = mysource.pixel( wctr ,hctr-1);
					x[4] = mysource.pixel(wctr-1,hctr-1);
					x[5] = mysource.pixel(wctr-1,hctr);
					x[6] = mysource.pixel(wctr-1,hctr+1);
					x[7] = mysource.pixel( wctr ,hctr+1);
					x[8] = mysource.pixel(wctr+1,hctr+1);*/
					x[1] = mysource.pixel(wctr+1,hctr);
					x[8] = mysource.pixel(wctr+1,hctr-1);
					x[7] = mysource.pixel( wctr ,hctr-1);
					x[6] = mysource.pixel(wctr-1,hctr-1);
					x[5] = mysource.pixel(wctr-1,hctr);
					x[4] = mysource.pixel(wctr-1,hctr+1);
					x[3] = mysource.pixel( wctr ,hctr+1);
					x[2] = mysource.pixel(wctr+1,hctr+1);

					index = 1*x[1] +2*x[2] +4*x[3] +8*x[4] +16*x[5] +32*x[6] +64*x[7]
					+128*x[8];

					// remove staircase
					if ((index == 5) ||
						(index == 20) ||
						(index == 80) ||
						(index == 65)) 
					{
						mysource.get_line(wctr, hctr, 1, &line, 0);
						line.pixels[0]=0;
						mysource.put_line(wctr, hctr, 1, &line, 0);
					}

					// remove extra points
					if ((x[1]+x[3]+x[5]+x[7]) == 3)
					{
						mysource.get_line(wctr, hctr, 1, &line, 0);
						line.pixels[0]=0;
						mysource.put_line(wctr, hctr, 1, &line, 0);
					}
				} // end if
		 }  // end for
	 }  // end for
	 // end stair case removal

	 //this loop reverses back the colors of the image 
	 for (int j = hctrUB-1; j >=0 ; --j)
	 {
		 mysource.get_line(0, j, wctrUB, &line, 0);

		 for(int s=0;s<wctrUB;s++)
			{
				if(line.pixels[s]==0)
					line.pixels[s]=1;
				else
					line.pixels[s]=0;
			}

		 mysource.put_line(0, j, wctrUB, &line, 0);
	 }				

	 //mysource.write("test_thn6.tif");


	 //Ahmad Sep 17, 2008
	 line.pixels[0] = thinned->get_white_level();
	 for(hctr = hctrUB - 1; hctr >= 0; hctr--)
		 for(wctr = wctrUB - 1; wctr >= 0; wctr--)
			 if (mysource.pixel(wctr,hctr) == mysource.get_white_level() &&
				 thinned->pixel(wctr,hctr) != thinned->get_white_level())
				 thinned->put_line(wctr,hctr,1,&line,0);
	 thinnedGreyLevel = !thinned->get_white_level();

	 if(keepOrigText) {
		 thinnedGreyLevel = 120;	//
		 line.pixels[0] = thinnedGreyLevel;
		 line2.pixels[0] = !thinned->get_white_level();
		 for(int xt = 0; xt < thinned->get_xsize(); xt++) {
			 for(int yt = 0; yt < thinned->get_ysize(); yt++) {
				 if(thinned->pixel(xt, yt) != thinned->get_white_level())
					 thinned->put_column(xt, yt, 1, &line, 0);
				 if(this->pixel(xt, yt) != this->get_white_level() && thinned->pixel(xt, yt) != thinnedGreyLevel)
					 thinned->put_column(xt, yt, 1, &line2, 0);
				}
			}
	 }
}//End of makeThinnedImage





/**********************************************************************
 * writeWithFeatures
 *
 * marks the detected features on the image 
 **********************************************************************/
void ARAB_IMAGE::writeWithFeatures() {
	PixelWithFeatures_IT features_it = &featurePointsLst;
	features_it.move_to_first();

	for(features_it.mark_cycle_pt(); !features_it.cycled_list(); features_it.forward()) {
		if(features_it.data()->feature == BRANCH_PT)
			putPixelThinned(features_it.data()->coords, 170);	
		else
			putPixelThinned(features_it.data()->coords, 220);	
	}
	//thinned->write("ThinnedWithFeatures.tif");		//Modify to have the fileName with it
}//End of writeWithFeatures


/**********************************************************************
 * putPixelThinned
 *
 * puts pixel on the THINNED image
 **********************************************************************/
inline void ARAB_IMAGE::putPixelThinned(ICOORD coords, BYTE greyLevel) {
	if(coords.x() < 0 || coords.x() > thinned->get_xsize() || coords.y() < 0 || coords.y() > thinned->get_ysize())
		return;
	IMAGELINE line;
	line.init();
	line.pixels[0] = greyLevel;
	thinned->fast_put_line(coords.x(), coords.y(), 1, &line);
}//End of putPixelThinned

/**********************************************************************
 * putPixelImage
 *
 * puts pixel on the image itself
 **********************************************************************/
inline void ARAB_IMAGE::putPixelImage(ICOORD coords, BYTE greyLevel) {
	if(coords.x() < 0 || coords.x() > this->get_xsize() || coords.y() < 0 || coords.y() > this->get_ysize())
		return;
	IMAGELINE line;
	line.init();
	line.pixels[0] = greyLevel;
	this->fast_put_line(coords.x(), coords.y(), 1, &line);
}//End of putPixelImage

/**********************************************************************
 * getRoutDiffs
 *
 * finds the first and the second differences of the coordinates stored for a route
 **********************************************************************/
void ARAB_IMAGE::getRoutDiffs(PixelWithDiffs_IT* pxDers_it) {
	int	pxCnt = 0;
	int routeLength = pxDers_it->length();

	if(routeLength < 4)
		return;

	int	X1, X2, Y1, Y2;

	pxDers_it->move_to_first();
	pxCnt = 0;
	for(pxDers_it->mark_cycle_pt(); !pxDers_it->cycled_list() && pxCnt < routeLength - 1; pxDers_it->forward()) {
		pxCnt++;
		X1 = pxDers_it->data()->coords.x();
		X2 = pxDers_it->data_relative(1)->coords.x();
		Y1 = pxDers_it->data()->coords.y();
		Y2 = pxDers_it->data_relative(1)->coords.y();

		pxDers_it->data()->firstDiff.set_x(X2 - X1);
		pxDers_it->data()->firstDiff.set_y(Y2 - Y1);
	}

	pxDers_it->move_to_first();
	pxCnt = 0;
	for(pxDers_it->mark_cycle_pt(); !pxDers_it->cycled_list() && pxCnt < routeLength - 2; pxDers_it->forward()) {
		pxCnt++;
		X1 = pxDers_it->data()->firstDiff.x();
		X2 = pxDers_it->data_relative(1)->firstDiff.x();
		Y1 = pxDers_it->data()->firstDiff.y();
		Y2 = pxDers_it->data_relative(1)->firstDiff.y();

		pxDers_it->data()->scndDiff.set_x(X2 - X1);
		pxDers_it->data()->scndDiff.set_y(Y2 - Y1);
	}	
}//End of getRoutDiffs

/**********************************************************************
 * getRouteLength
 *
 * finds the number of pixels between PT1 and PT2 belonging to the list of pixels passed
 **********************************************************************/
int ARAB_IMAGE::getRouteLength(ICOORD PT1, ICOORD PT2, PixelWithDiffs_LIST* pxDers_LIST) {
	PixelWithDiffs_IT	pxDers_it = pxDers_LIST;
	int					rtLength = 0;
	bool				traceBack = false;
	ICOORD				firstPixel;
	ICOORD				lastPixel;

	pxDers_it.move_to_first();
	firstPixel = pxDers_it.data()->coords;
	pxDers_it.move_to_last();
	lastPixel = pxDers_it.data()->coords;

	if(PT1 == firstPixel && PT2 == lastPixel)
		return pxDers_it.length();

	if(PT1 == firstPixel && PT2 != lastPixel) {
		pxDers_it.move_to_first();
		while(pxDers_it.data()->coords != PT2) {
			rtLength++;
			pxDers_it.forward();
		}
	}
	else if	(PT2 == firstPixel && PT1 != lastPixel) {
		pxDers_it.move_to_first();
		while(pxDers_it.data()->coords != PT1) {
			rtLength++;
			pxDers_it.forward();
		}
	}
	else if (PT1 == lastPixel && PT2 != firstPixel) {
		pxDers_it.move_to_last();
		while(pxDers_it.data()->coords != PT2) {
			rtLength++;
			pxDers_it.backward();
		}
	}
	else if(PT2 == lastPixel && PT1 != firstPixel) {
		pxDers_it.move_to_last();
		while(pxDers_it.data()->coords != PT1) {
			rtLength++;
			pxDers_it.backward();
		}
	} else {
		pxDers_it.move_to_first();
		while(pxDers_it.data()->coords != PT1)
			pxDers_it.forward();

		while(pxDers_it.data()->coords != PT2) {
			rtLength++;
			pxDers_it.forward();
			if(pxDers_it.at_last()) {
				rtLength = 0;
				traceBack = true;
			}
		}

		if(traceBack) {
			pxDers_it.move_to_first();
			while(pxDers_it.data()->coords != PT1)
				pxDers_it.forward();

			while(pxDers_it.data()->coords != PT2) {
				rtLength++;
				pxDers_it.backward();
			}
		}
	}

	return rtLength;
}//End of getRouteLength

/**********************************************************************
 * findRoutes
 *
 * finds the coordinates of the routes and stores them in origRouteList
 **********************************************************************/
void ARAB_IMAGE::findRoutes() {
	PixelWithFeatures_IT		features_it = &featurePointsLst;
	Route_IT					routes_it = &origRouteList;
	BYTE						N8[8];
	BYTE						tmpFeature;

	//pxDers_it->set_to_list(tmpRoutePtr->getPixelWithDiffsList());

	origRouteList.clear();

	findSimpleFeatures();	//must be invoked before the routes are detected, each route is defined as a stream of pixels
							//between two simple features

	features_it.move_to_first();
	for(features_it.mark_cycle_pt(); !features_it.cycled_list(); features_it.forward()) {
		if(features_it.data()->feature == BRANCH_PT
		|| features_it.data()->feature == CROSS_PT 
		|| features_it.data()->feature == END_PT) {
			for(int i = 0; i < 8; i++) {		//Analyzing possible continuities with 8 neighborhood connectivity
				getN8(features_it.data()->coords, N8, thinned);
				if(N8[i] == thinnedGreyLevel) {
					//int testt = features_it.data()->coords.x();
					Route*	tmpRoutePtr = new Route();
					getRoute(features_it.data()->coords, i, tmpRoutePtr);	//based on the direction of movement for the next pixel, getRoute
																			//detects the continous stream of pixels going from the current 
																			//simple feature 
					routes_it.add_after_then_move(tmpRoutePtr);
				}
			}
		}
	}
}//End of findRoutes


/**********************************************************************
 * findSimpleFeatures
 *
 * finds the number of pixels between PT1 and PT2 belonging to the list of pixels passed
 **********************************************************************/
void ARAB_IMAGE::findSimpleFeatures() {
	PixelWithFeatures_IT		features_it = &featurePointsLst;
	featurePointsLst.clear();
	BYTE						N8[8];
	BYTE						tmpFeature;

	//thinned->write("test1.tif");
	for(int xt = 0; xt < thinned->get_xsize(); xt++) {			//Extracting simple features, namely, END, BRANCH, CROSS points
		for(int yt = 0; yt < thinned->get_ysize(); yt++) {
			if(thinned->pixel(xt, yt) == thinnedGreyLevel) {	
				getN8(ICOORD(xt, yt), N8, thinned); 
				tmpFeature = findSimpleFeature(N8, thinnedGreyLevel);
				if(tmpFeature != NOT_FEATURE_PT) {
					features_it.add_after_then_move(new PixelWithFeatures(ICOORD(xt, yt), tmpFeature));
				}
			}
		}
	}
	
	features_it.move_to_first();
	for(features_it.mark_cycle_pt(); !features_it.cycled_list(); features_it.forward()) {
		this->putPixelThinned(features_it.data()->coords, features_it.data()->feature);	//Marking the feature point on the image for later work
																					//IMPORTANT for getRoute to perform well
	}
}//End of findSimpleFeatures


/**********************************************************************
 * getRoute
 *
 * fills the route pixels list pointed to by routePtr, starting from direction. Refer to getN8 for details about direction
 **********************************************************************/
void ARAB_IMAGE::getRoute(ICOORD coords, int dir, Route* routePtr) {
	PixelWithDiffs_IT*			pxDers_it = new PixelWithDiffs_IT(&(routePtr->coordsList));
	PixelWithDiffs				tmpPixelWithDiffs;
	BYTE						N8[8];
	int							pxCount = 0;
	BYTE						whiteLevel = this->get_white_level();
	bool						N8GotFeature = false;
	int							i = 0;	//general loopcounter
	int							tmpDir = 0;
	ICOORD						tmpCoords(0, 0);	
	int							numOfFeatsInN8 = 0;


	routePtr->firstPt.coords = coords;
	routePtr->firstPt.feature = thinned->pixel(coords.x(), coords.y());

	////TEST::
	//int dum1 = coords.x();
	////

	pxDers_it->add_after_then_move(new PixelWithDiffs(coords));
	getDirCoords(dir, coords);

	//if N8 got two features then the current route is a 3 pixels route and exit this function
	numOfFeatsInN8 = 0;
	getN8(coords, N8, thinned);
	for(int i = 0; i < 8; i++) {
		if(N8[i] == BRANCH_PT
		||N8[i] == CROSS_PT
		||N8[i] == END_PT) {
			numOfFeatsInN8++;
		}

	}

	if(numOfFeatsInN8 > 1) {
		routePtr->lastPt = routePtr->firstPt;
		return;
	}

	do {
		//int ddd = coords.x();
		pxDers_it->add_after_then_move(new PixelWithDiffs(coords));
		if(thinned->pixel(coords.x(), coords.y()) == thinnedGreyLevel)
			this->putPixelThinned(coords, VISITED_PT);
		getN8(coords, N8, thinned);
		if(N8GotSimpleFeature(N8) && pxCount > 0)
			dir = -1;
		else {
			for(int i = 0; i < 8; i++) {
				if(N8[i] == thinnedGreyLevel) {
					dir = i;
					break;
				}
			}
		}
		getDirCoords(dir, coords);
		pxCount++;
	}while(dir != -1);

	//Adding the last pixel i.e the other feature point in the continuity
	for(int i = 0; i < 8; i++) {
		if(N8[i] == END_PT || N8[i] == BRANCH_PT || N8[i] == CROSS_PT) {
			dir = i;
			break;
		}
	}
	getDirCoords(dir, coords);
	pxDers_it->add_after_then_move(new PixelWithDiffs(coords));


	//getRoutDiffs(pxDers_it);

	routePtr->lastPt.coords = coords;
	routePtr->lastPt.feature = thinned->pixel(coords.x(), coords.y());

	////TEST
	//FILE* fOut;
	//fOut = fopen("testRoutes.txt", "w");	
	//
	//pxDers_it->move_to_first();
	//for(pxDers_it->mark_cycle_pt(); !pxDers_it->cycled_list(); pxDers_it->forward()) {
	//	PixelWithDiffs tmp = *(pxDers_it->data());
	//	fprintf(fOut,"%d, %d,  \t%d, %d,  \t%d, %d \n", tmp.coords.x(), (this->get_ysize() - tmp.coords.y()) - 1, tmp.firstDiff.x(), tmp.firstDiff.y(), tmp.scndDiff.x(), tmp.scndDiff.y());
	//}	
	//
	//fclose(fOut);
	////
}//End of getRoute


/**********************************************************************
 * routeToPolygon
 *
 * for a list of contiguous pixels (like that stored in each route), this function
 * produces a list of edges, each with an angle and bisectorAngle
 **********************************************************************/
void ARAB_IMAGE::routeToPolygon(PixelWithDiffs_LIST* pxDers_list, PolygonalEdge_LIST* poly_list, double maxErrorDist) {
	PixelWithDiffs_IT pxDers_it = pxDers_list;
	int			routeLength = pxDers_it.length();
	int			relativeDisp = 0;
	int			MIN_DIST_FROM_END = 3;
	double		maxDistLineCont = 0;
	double		theta1 = 0;
	double		theta2 = 0;
	double		largerTheta = 0;
	double		smallerTheta = 0;
	ICOORD		lastPixel;
	ICOORD		E1(0, 0);
	ICOORD		E1_tmp(0, 0);
	PolygonalEdge_IT poly_it = poly_list;
	int			edgeCnt = 0;
	int			polyLength = 0;
	double		deltaY = 0;
	double		deltaX = 0;
	double		deltaTheta = 0;
	double		relativeDistBackFactor = 0.15;
	int			MAX_BACK = 8;

	double		sumDist = 0; 


	if(routeLength < 10)
		return;

	pxDers_it.move_to_last();
	lastPixel = pxDers_it.data()->coords;

	pxDers_it.move_to_first();
	int tstX = pxDers_it.data()->coords.x();
	poly_it.add_after_then_move(new PolygonalEdge(pxDers_it.data()->coords));
	while(E1 != lastPixel) {
		relativeDisp = 0;
		maxDistLineCont = 0;
		//sumDist = 0;

		//TEST///////////////DELTA LENTh LINE ROUTE
		//E1_prev = pxDers_it.data()->coords;
		//do {
		//	E1 = pxDers_it.data_relative(relativeDisp)->coords;
		//	lineDist = E1_prev.pt_to_pt_dist(E1);
		//	routeDist = getRouteLength(E1_prev, E1, pxDers_list);
		//	relativeDisp++;
		//} while(routeDist - lineDist < 1);
		/////////////////////
		while((maxDistLineCont < maxErrorDist) && (E1 != lastPixel)) {//while((sumDist < 5) && (E1 != lastPixel)) {
			//sumDist = 0;
			for(int i = relativeDisp; i > 0; i--) {
				maxDistLineCont = max(maxDistLineCont, findDistLinePt(pxDers_it.data()->coords, E1, pxDers_it.data_relative(i)->coords));
				//sumDist += findDistLinePt(pxDers_it.data()->coords, E1, pxDers_it.data_relative(i)->coords);
			}
			E1 = pxDers_it.data_relative(relativeDisp)->coords;
			relativeDisp++;
		}
		if(getRouteLength(E1, lastPixel, pxDers_list) > MIN_DIST_FROM_END) {
			E1_tmp = pxDers_it.data_relative
				(relativeDisp - (relativeDistBackFactor * relativeDisp))->coords;
			poly_it.add_after_then_move(new PolygonalEdge(E1));//whic is better, E1 or E1_tmp
		}

		while(pxDers_it.data()->coords != E1)
			pxDers_it.forward();
	}
	poly_it.add_after_then_move(new PolygonalEdge(lastPixel));

	polyLength = poly_it.length();
	poly_it.move_to_first();
	poly_it.data()->angle = 10;
	poly_it.forward();
	edgeCnt++;
	while(edgeCnt++ < polyLength - 1) {
		ICOORD tmpCo = poly_it.data()->coords;
		deltaY = poly_it.data_relative(-1)->coords.y() - poly_it.data()->coords.y();
		deltaX = poly_it.data_relative(-1)->coords.x() - poly_it.data()->coords.x();
		theta1 = atan2(deltaY, deltaX);
		if(theta1 < 0)
			theta1 += (D360);

		deltaY = poly_it.data_relative(1)->coords.y() - poly_it.data()->coords.y();
		deltaX = poly_it.data_relative(1)->coords.x() - poly_it.data()->coords.x();
		theta2 = atan2(deltaY, deltaX);
		if(theta2 < 0)
			theta2 += (D360);
		largerTheta = (theta1 >= theta2 ? theta1 : theta2);
		smallerTheta = (theta1 <= theta2 ? theta1 : theta2);
		deltaTheta = largerTheta - smallerTheta;
		if(deltaTheta > D180) {
			deltaTheta = D360 - deltaTheta;
			poly_it.data()->bisectorAngle = (largerTheta + smallerTheta)/2 + D180;
		}
		else
			poly_it.data()->bisectorAngle = (largerTheta + smallerTheta)/2;

		poly_it.data()->angle = deltaTheta;

		if(poly_it.data()->bisectorAngle > D360)
			poly_it.data()->bisectorAngle -= D360;

		int stest = poly_it.data()->coords.x();
		poly_it.forward();
	}
	poly_it.data()->angle = 10;
}//End of routeToPolygon

/**********************************************************************
 * detectLoops
 *
 * Flags the routes included in a loop as a partOfLoop.
 * This function detects loops of up to degree 3. The degree of the loop is the number of routes
 * contains in the loop
 **********************************************************************/
void ARAB_IMAGE::detectLoops() {
	Route_IT	route_it = &origRouteList;
	Route_IT	route_it2 = &origRouteList;
	Route_IT	route_it3 = &origRouteList;
	ICOORD		E1_1;
	ICOORD		E1_2;
	ICOORD		E2_1;
	ICOORD		E2_2;
	ICOORD		E3_1;
	ICOORD		E3_2;
	
	route_it.move_to_first();
	for(route_it.mark_cycle_pt(); !route_it.cycled_list(); route_it.forward()) {
		if((route_it.data()->firstPt.feature == BRANCH_PT
		|| route_it.data()->firstPt.feature == CROSS_PT)
		&& (route_it.data()->lastPt.feature == BRANCH_PT
		|| route_it.data()->lastPt.feature == CROSS_PT)) {
				E1_1 = route_it.data()->firstPt.coords;
				E1_2 = route_it.data()->lastPt.coords;
				if(absFJ(E1_1 - E1_2) < 3)
					route_it.data()->isPartOfLoop = true;
				else {
					route_it.data()->isInLoopDetection = true;
					for(route_it2.mark_cycle_pt(); !route_it2.cycled_list(); route_it2.forward()) {
						if((route_it2.data()->firstPt.feature == BRANCH_PT
						|| route_it2.data()->firstPt.feature == CROSS_PT)
						&& (route_it2.data()->lastPt.feature == BRANCH_PT
						|| route_it2.data()->lastPt.feature == CROSS_PT)
						&& !route_it2.data()->isInLoopDetection) {
								route_it2.data()->isInLoopDetection = true;					
								E2_1 = route_it2.data()->firstPt.coords;
								E2_2 = route_it2.data()->lastPt.coords;
								if((absFJ(E1_1 - E2_1) < 3 || absFJ(E1_1 - E2_2) < 3)
								&& (absFJ(E1_2 - E2_1) < 3 || absFJ(E1_2 - E2_2) < 3)) {
										route_it.data()->isPartOfLoop = true;
										route_it2.data()->isPartOfLoop = true;
								}
								else {
									for(route_it3.mark_cycle_pt(); !route_it3.cycled_list(); route_it3.forward()) {
										if((route_it3.data()->firstPt.feature == BRANCH_PT
										|| route_it3.data()->firstPt.feature == CROSS_PT)
										&& (route_it3.data()->lastPt.feature == BRANCH_PT
										|| route_it3.data()->lastPt.feature == CROSS_PT)
										&& !route_it3.data()->isInLoopDetection) {
												route_it3.data()->isInLoopDetection = true;
												E3_1 = route_it3.data()->firstPt.coords;
												E3_2 = route_it3.data()->lastPt.coords;							

												if( (absFJ(E1_1 - E2_1) < 3 && (absFJ(E1_2 - E3_1) < 3 || absFJ(E1_2 - E3_2) < 3) && (absFJ(E2_2 - E3_1) < 3 || absFJ(E2_2 - E3_2) < 3))
												||	(absFJ(E1_2 - E2_2) < 3 && (absFJ(E1_1 - E3_1) < 3 || absFJ(E1_1 - E3_2) < 3) && (absFJ(E2_1 - E3_1) < 3 || absFJ(E2_1 - E3_2) < 3))
												||	(absFJ(E1_1 - E2_2) < 3 && (absFJ(E1_2 - E3_1) < 3 || absFJ(E1_2 - E3_2) < 3) && (absFJ(E2_1 - E3_1) < 3 || absFJ(E2_1 - E3_2) < 3))
												||	(absFJ(E1_2 - E2_1) < 3 && (absFJ(E1_1 - E3_1) < 3 || absFJ(E1_1 - E3_2) < 3) && (absFJ(E2_2 - E3_1) < 3 || absFJ(E2_2 - E3_2) < 3))
												) {
														route_it.data()->isPartOfLoop = true;
														route_it2.data()->isPartOfLoop = true;
														route_it3.data()->isPartOfLoop = true;
												}
												route_it3.data()->isInLoopDetection = false;
										}
									}
									route_it2.data()->isInLoopDetection = false;
								}
						}
					}
					route_it.data()->isInLoopDetection = false;
				}
		}
	}
}//End of detectLoops



/**********************************************************************
 * findEdgeFeatures
 *
 * detecting the edges that are to be used as features, those edges are a filtered
 * version of the edges in the polygonal approximation
 **********************************************************************/
void ARAB_IMAGE::findEdgeFeatures() {
	PixelWithFeatures_IT	features_it = &featurePointsLst;
	PolygonalEdge_LIST		poly_list;
	PolygonalEdge_IT		poly_it = &poly_list;
	Route_IT				route_it = &routeList;
	double					MAX_ANGLE = 2.3;
	double					MAX_DIST = 3.25;
	double					MIN_ANGLE = 1.75;
	bool					isEdgePt = false;
	PixelWithFeatures		minDistPT(ICOORD(0, 0), 0);


#ifdef MAX_POLY_EDGE_ANGLE_TO_KEEP
	MAX_ANGLE = MAX_POLY_EDGE_ANGLE_TO_KEEP;
#endif

#ifdef POLYGONAL_MAX_DIST_TO_STOP
	MAX_DIST = POLYGONAL_MAX_DIST_TO_STOP;
#endif

#ifdef POLYGONAL_MAX_DIST_TO_STOP
	MIN_ANGLE = MIN_ANGLE_LOC_MIN;
#endif

	features_it.move_to_last();


	route_it.move_to_last();
	do {
		route_it.forward();
		if(!(route_it.data()->isPartOfLoop)) {
			isEdgePt = false;
			routeToPolygon(route_it.data()->getPixelWithDiffsList(), &poly_list, MAX_DIST);
			poly_it.move_to_first();
			for(poly_it.mark_cycle_pt(); !poly_it.cycled_list() && !isEdgePt; poly_it.forward()) {

				//int sscc = route_it.data()->firstPt.coords.x();
				//int xxc1 = poly_it.data()->coords.x();
				//double bsangl1 = poly_it.data()->bisectorAngle;
				//double bbangll = poly_it.data()->angle;
				//int dum = 33;

				if(poly_it.data()->angle < MAX_ANGLE && poly_it.data()->angle != 10) {//IMP:: != 10 means not first nor last point of the polygon approximation (&&poly_it.data()->angle < MAX_ANGLE && )
					PixelWithFeatures*	pxWthFeatsPtr = new PixelWithFeatures(poly_it.data()->coords, EDGE_PT, poly_it.data()->bisectorAngle);
					int xxc = poly_it.data()->coords.x();
					//double bsangl = poly_it.data()->bisectorAngle;
					//features_it.add_after_then_move(pxWthFeatsPtr);
					minDistPT = getRouteLength(poly_it.data()->coords, route_it.data()->firstPt.coords, route_it.data()->getPixelWithDiffsList()) > getRouteLength(poly_it.data()->coords, route_it.data()->lastPt.coords, route_it.data()->getPixelWithDiffsList())?route_it.data()->lastPt:route_it.data()->firstPt;
					if(!(isAnglesEqual(poly_it.data()->bisectorAngle, D90, .45) && poly_it.data()->angle > MIN_ANGLE)
					&& !(isAnglesEqual(poly_it.data()->bisectorAngle, D270, .45) && poly_it.data()->angle > MIN_ANGLE * 1.5)
					&& !(getRouteLength(minDistPT.coords, poly_it.data()->coords, route_it.data()->getPixelWithDiffsList()) <= 10 && abs(minDistPT.coords.x() - poly_it.data()->coords.x()) <= 5 && minDistPT.feature != END_POINT)
					)//FUAD:: LAST: MAY CAUSE DIFFERENT RESULTS
					{
						isEdgePt = true;
						splitRoute(&route_it, *pxWthFeatsPtr);
						features_it.add_after_then_move(pxWthFeatsPtr);
					}
				}
			}
			poly_list.clear();
		}
	} while(!route_it.at_last());
	
	poly_list.clear();
}//End of findEdgeFeatures


/**********************************************************************
 * findSubtleBranchFeatures
 *
 * a subtle branch is identified as a local max in the thinned image
 **********************************************************************/
void ARAB_IMAGE::findSubtleBranchFeatures() {
	PixelWithFeatures_IT	features_it = &featurePointsLst;
	Route_IT				route_it = &origRouteList;
	PixelWithDiffs_IT*		pxDers_it = new PixelWithDiffs_IT;
	bool					isSubtleBranch = false;
	bool					dontMark = false;
	int						routeLength = 0;
	int						relativeDisp = 0;
	int						currentY = 0, currentX = 0;
	int						prevY = 0;
	int						nextY = 0;
	int						pxCnt = 0;
	int						lengthOfStraightness = 0;
	int						pxCntBelowCandidateSBranch = 0;
	int						pxCntAboveCandidateSBranch = 0;
	int						i = 0, j = 0;	//general purpose
	int						MIN_Y_DIFF = 0;
	int						MAX_Y_DIFF = 0;
	int						MIN_X_DIFF = 0;
	int						xT = 20;//size of half-window
	int						realDistLeftCurrent = 0;
	int						realDistRightCurrent = 0;



#ifdef MIN_Y_DIFF_TO_CONSIDER_SUBTLE_BRNACH
	MIN_Y_DIFF = MIN_Y_DIFF_TO_CONSIDER_SUBTLE_BRNACH;
#endif

#ifdef MAX_Y_DIFF_TO_CONSIDER_SUBTLE_BRNACH
	MAX_Y_DIFF = MAX_Y_DIFF_TO_CONSIDER_SUBTLE_BRNACH;
#endif

#ifdef HALF_WINDOW_WIDTH_SUBTLE_BRANCH
	xT = HALF_WINDOW_WIDTH_SUBTLE_BRANCH;
#endif

	features_it.move_to_last();

	route_it.move_to_last();
	do {
		route_it.forward();
		if(!(route_it.data()->isPartOfLoop)) {
			isSubtleBranch = false;
			dontMark = false;

			//int testt1 = route_it.data()->firstPt.coords.x();

			pxDers_it->set_to_list(route_it.data()->getPixelWithDiffsList());
			routeLength = pxDers_it->length();
			pxDers_it->move_to_first();
			pxCnt = 0;
			while(!pxDers_it->at_last() && !isSubtleBranch) {
				//TEST
				//int dum1 = pxDers_it->data()->coords.x();
				////
				pxCnt++;
				currentY = pxDers_it->data()->coords.y();
				currentX = pxDers_it->data()->coords.x();
				prevY = currentY;
				nextY = currentY;

				relativeDisp = -1;
				while(relativeDisp > -xT && (relativeDisp + pxCnt > 0)
				&& pxDers_it->data_relative(relativeDisp)->coords.y() <= prevY) {
					prevY = pxDers_it->data_relative(relativeDisp)->coords.y();
					relativeDisp--;
				}

				realDistLeftCurrent = abs(relativeDisp);
				relativeDisp = 1;
				while(relativeDisp < xT && (relativeDisp + pxCnt < routeLength) 
				&& pxDers_it->data_relative(relativeDisp)->coords.y() <= nextY) {
					nextY = pxDers_it->data_relative(relativeDisp)->coords.y();
					relativeDisp++;
				}

				realDistRightCurrent = relativeDisp;

				if((currentY  - prevY > MIN_Y_DIFF) 
				&& (currentY  - prevY < MAX_Y_DIFF) 
				&& (currentY  - nextY > MIN_Y_DIFF) 
				&& (currentY  - nextY < MAX_Y_DIFF)
				//&& (nextY - prevY  < max(realDistLeftCurrent, realDistRightCurrent) / 4)
				) {
					//pxCntBelowCandidateSBranch = 0;
					isSubtleBranch = true;
					lengthOfStraightness = 0;
					while(pxDers_it->data()->coords.y() == currentY && !pxDers_it->at_last()) {
						pxDers_it->forward();
						pxCnt++;
						lengthOfStraightness++;
					}
				}

				if(isSubtleBranch) {
					PixelWithFeatures*	pxWthFeatsPtr = new PixelWithFeatures(pxDers_it->data_relative(-(lengthOfStraightness/2 + 1))->coords, SUBTLE_BRANCH_PT);
					features_it.add_after_then_move(pxWthFeatsPtr);
					for(int i = 0; i < xT - lengthOfStraightness; i++) {
						if(!pxDers_it->at_last()) {
							pxDers_it->forward();
							pxCnt++;
						}
					}
					splitRoute(&route_it,*pxWthFeatsPtr);
				}

				if(!pxDers_it->at_last() && !isSubtleBranch)
					pxDers_it->forward();
			}
		}
	} while(!route_it.at_last());

	//Fuad:: temp code: copying the original list of routes to the new one that is to be used for splitting at edges
	copyRoutesList(&origRouteList, &routeList);
	////
}//End of findSubtleBranchFeatures


/**********************************************************************
 * splitRoute
 *
 * splits the current route pointed to by the iterator passed to two routes
 * at PT1, the iterator is then moved the new route (before the original route)
 * that is needed to be able to split the original route again if needed
 **********************************************************************/
void ARAB_IMAGE::splitRoute(Route_IT* route_it_Ptr, PixelWithFeatures PT1) {
	Route*				route1Ptr = new Route();
	PixelWithDiffs_IT	pxDers_it = route_it_Ptr->data()->getPixelWithDiffsList();
	PixelWithDiffs_IT	pxDers_it1 = route1Ptr->getPixelWithDiffsList();
	int					MIN_LENGTH_TO_CONSIDER = 10;
	int					MIN_DELTA_Y = 10;//to tell how horizontal the route is

	route1Ptr->firstPt = route_it_Ptr->data()->firstPt;
	route1Ptr->lastPt = PT1;
	route_it_Ptr->data()->firstPt = PT1;

	pxDers_it.move_to_first();
	while(pxDers_it.data()->coords != PT1.coords) {
		ICOORD tmp = pxDers_it.data()->coords;
		pxDers_it1.add_after_then_move(pxDers_it.extract());
		pxDers_it.forward();
	}
	pxDers_it1.add_after_then_move(new PixelWithDiffs(pxDers_it.data()->coords));

	route_it_Ptr->add_before_then_move(route1Ptr);
}//End of splitRoute


/**********************************************************************
 * findSegmentationPoints
 *
 * Based on the information collected and stored in the routes list, this functions selects the best segmentation
 * points contained in the routes and stores them in segmentationCoordsList
 **********************************************************************/
void ARAB_IMAGE::findSegmentationPoints(IMAGE* coloredCmpsImg) {
	Route_IT				routeNoEdges_it = &origRouteList;
	SEG_POINT_IT			segPts_it = &segmentationCoordsList;
	PixelWithFeatures_IT	featuresInImage_it;
	PixelWithFeatures_IT	featuresInCmp_it;
	Route_IT				routesInImage_it = &routeList;
	Route_IT				routesInCmp_it;

	//TEST
	const char* fileName = strrchr(__argv[1], '\\');
	fileName++;
	char* WDETAILS = new char[50];
	strcpy(WDETAILS, "");
	strcat(WDETAILS, "WDETAILS_");
	strcat(WDETAILS, fileName);
	////

	//Statistics
	/*STATS	widthBefSeg(0, 200);
	STATS	widthAfterSeg(0, 200);
	STATS	heightBefSeg(0, 200);
	STATS	heightAfterSeg(0, 200);

	for(int i = 1; i <= numOfCmps_BeforeSeg; i++) {
		widthBefSeg.add(connCmpPtr_BeforeSeg[i].width, 1);
		heightBefSeg.add(connCmpPtr_BeforeSeg[i].height, 1);
	}

	double	avgWidthBefSeg = widthBefSeg.mean();
	double	avgHeightBefSeg = heightBefSeg.mean();
	int		medianWidthBefSeg = widthBefSeg.median();
	int		medianHeightBefSeg = heightBefSeg.median();
	///*/


	//Filling the features and routes information for the connected components
	PixelWithFeatures_LIST		tmpPixList;
	copyFeaturesList(&featurePointsLst, &tmpPixList);

	featuresInImage_it.set_to_list(&tmpPixList);
	for(int i = 1; i <= numOfCmps_BeforeSeg; i++) {
		featuresInCmp_it.set_to_list(&(connCmpPtr_BeforeSeg[i].featurePointsLst));
		for(featuresInImage_it.mark_cycle_pt(); !featuresInImage_it.cycled_list(); featuresInImage_it.forward()) {
			if(coloredCmpsImg->pixel(featuresInImage_it.data()->coords.x(), featuresInImage_it.data()->coords.y()) == i) {
				featuresInCmp_it.add_after_then_move(featuresInImage_it.extract());
			}
		}
		routesInCmp_it.set_to_list(&(connCmpPtr_BeforeSeg[i].routeList));
		for(routesInImage_it.mark_cycle_pt(); !routesInImage_it.cycled_list(); routesInImage_it.forward()) {
			if(coloredCmpsImg->pixel(routesInImage_it.data()->firstPt.coords.x(), routesInImage_it.data()->firstPt.coords.y()) == i) {
				routesInCmp_it.add_after_then_move(routesInImage_it.extract());
			}
		}
	}
	tmpPixList.clear();

	segmentationCoordsList.clear();
	for(int i = 1; i <= numOfCmps_BeforeSeg; i++) {
		findSegPointsInComponent(i, connCmpPtr_BeforeSeg, &segPts_it);
	}

#ifdef	WRITE_IMAGE_WDETAILS
	writeImgWithAllDetails(WDETAILS);
#endif
	
	delete []WDETAILS;

}//End of findSegmentationPoints

/**********************************************************************
 * findCutPosition
 *
 * This function acts like a filter for the detected segmentation points.
 * >>returns false for a cut point that is detected in a route already containing a cut point
 * to avoid over segmentation
 * >>returns false for a cut point that is far from the baseline (not used currently)
 * >>returns true for a cut point that is valid and the cut point is selected to be the
 * >>returns false for a cut point detected inside complex shapes (the vertical extention of the cut point hits the thinned image)
 * mid point of the route
 **********************************************************************/
bool ARAB_IMAGE::findCutPosition(Route* routePtr, ICOORD* cutPositionPtr) {
	PixelWithDiffs_IT		pxDers_it = routePtr->getPixelWithDiffsList();
	int						routeLength = pxDers_it.length();
	int						pxCnt = 0;
	int						MAX_BL = BaseLine + (this->get_ysize()- 40)/2;
	int						MIN_BL = BaseLine - (this->get_ysize() - 40)/2;
	bool					gotBodyAbove = true;
	bool					gotBodyBelow = true;
	BYTE					WHITE = get_white_level();
	int						xt = 0, yt = 0;
	int						maxY = get_ysize();
	int						minY = 0;
	int						tmpSlope = 1;
	int						tmpInd = 0;
	int						numOfPixToConsiderHor = 2;
	int						strokeWidth = 0;
	
	cutPositionPtr->set_x(0);
	cutPositionPtr->set_y(0);


	//check for previous cut position in the candidate route
	for(pxDers_it.mark_cycle_pt(); !pxDers_it.cycled_list(); pxDers_it.forward()) {
		if(thinned->pixel(pxDers_it.data()->coords.x(), pxDers_it.data()->coords.y()) == CUT_PT) {
			return false;
		}
	}

	pxDers_it.move_to_first();
	//pxDers_it.forward();//don't consider left most point of the route
	while((!pxDers_it.at_last()) && (gotBodyAbove || gotBodyBelow)) {
		gotBodyAbove = false;
		gotBodyBelow = false;
		xt = pxDers_it.data()->coords.x();
		yt = pxDers_it.data()->coords.y();
		//maxY = yt + 50 //let it be certain distance from the the current y
		//minY = yt - 50 //let it be certain distance from the the current y
		do {
			if(thinned->pixel(xt, ++yt) != WHITE) {
				gotBodyAbove = true;
			}
		} while(!gotBodyAbove && yt < maxY);
		yt = pxDers_it.data()->coords.y();
		do {
			if(thinned->pixel(xt, --yt) != WHITE) {
				gotBodyBelow = true;
			}
		} while(!gotBodyBelow && yt > 0);
		pxDers_it.forward();
	} 

	tmpSlope = 1;
	pxDers_it.move_to_first();

	for(tmpInd = 0; tmpInd < routeLength/4; tmpInd++) {pxDers_it.forward();}
	if(!gotBodyAbove && !gotBodyBelow) {
		while(
			(tmpSlope = pxDers_it.data_relative(-numOfPixToConsiderHor)->coords.y() - pxDers_it.data_relative(numOfPixToConsiderHor)->coords.y()) != 0
			&& tmpInd++ < routeLength - max((numOfPixToConsiderHor + 1), routeLength/4)
		) {pxDers_it.forward();}
		if(tmpSlope != 0) {
			pxDers_it.move_to_first();
			tmpInd = 0;
			for(tmpInd = 0; tmpInd < routeLength/4; tmpInd++) {pxDers_it.forward();}
			numOfPixToConsiderHor--;
			while(
				(tmpSlope = pxDers_it.data_relative(-numOfPixToConsiderHor)->coords.y() - pxDers_it.data_relative(numOfPixToConsiderHor)->coords.y()) != 0
				&& tmpInd++ < routeLength - max((numOfPixToConsiderHor + 1), routeLength/5)
			) {pxDers_it.forward();}
		}
		if(tmpSlope == 0) {
			cutPositionPtr->set_x(pxDers_it.data()->coords.x());
			cutPositionPtr->set_y(pxDers_it.data()->coords.y());
			putPixelThinned(*cutPositionPtr, CUT_PT);
		} 
		else {
			pxDers_it.move_to_first();
			cutPositionPtr->set_x(pxDers_it.data_relative(routeLength/2)->coords.x());
			cutPositionPtr->set_y(pxDers_it.data_relative(routeLength/2)->coords.y());
			putPixelThinned(*cutPositionPtr, CUT_PT);
		}
	}


	//pxDers_it.move_to_first();
	//while(pxCnt++ < routeLength/2)
	//	pxDers_it.forward();

	//cutPositionPtr->set_x(pxDers_it.data()->coords.x());
	//cutPositionPtr->set_y(pxDers_it.data()->coords.y());

	//finding the stroke width in the segmentation position
	yt = cutPositionPtr->y();
	xt = cutPositionPtr->x();
	strokeWidth = 0;
	while(pixel(xt, yt++) != WHITE && yt < get_ysize())
		strokeWidth++;
	yt = cutPositionPtr->y();
	while(pixel(xt, --yt) != WHITE && yt > 0)
		strokeWidth++;
	if(strokeWidth > 3 * AVG_STROKE_WIDTH) {
		return false;
	}


	if(cutPositionPtr->y() > MAX_BL || cutPositionPtr->y() < MIN_BL)
		return false;

	return true;
}//End of findCutPosition

/**********************************************************************
 * markSegPointsOnImage
 *
 **********************************************************************/
void ARAB_IMAGE::markSegPointsOnImage(IMAGE* destImg) {
	SEG_POINT_IT	coords_it = &segmentationCoordsList;
	int				dirOfCut = 0;//1:Vertical     2:-fourtyFive      3:fourtyFive        4:Horizontal
	int				cnt = 0;
	int				xCut = 0;
	int				yCut = 0;
	int				imgX = get_xsize();
	int				imgY = get_ysize();
	int				xt = 0;
	int				yt = 0;
	BYTE			WHITE = get_white_level();
	IMAGELINE		line;
	line.init();
	line.pixels[0] = WHITE;


	coords_it.move_to_first();
	for(coords_it.mark_cycle_pt(); !coords_it.cycled_list(); coords_it.forward()) {
		xCut = coords_it.data()->x();
		yCut = coords_it.data()->y();
		if(xCut != 0 && yCut != 0) {
			xt = xCut;
			yt = yCut;
			while(destImg->pixel(xt, yt) != WHITE && abs(yt - yCut) < 9) {
				destImg->put_column(xt, yt++, 1, &line, 0);
			}
			xt = xCut;
			yt = yCut - 1;
			while(destImg->pixel(xt, yt) != WHITE && abs(yt - yCut) < 9) {
				destImg->put_column(xt, yt--, 1, &line, 0);
			}
		}
	}
}//End of markSegPointsOnImage

/**********************************************************************
 * getBisectorAngle
 *
 **********************************************************************/
double ARAB_IMAGE::getBisectorAngle(ICOORD pt_prev, ICOORD pt, ICOORD pt_next) {
	double		angle = 0;
	double		bisectorAngle = 0;
	double		smallerTheta = 0;
	double		largerTheta = 0;
	double		deltaTheta = 0;
	double		deltaY = 0;
	double		deltaX = 0;
	double		theta1 = 0;
	double		theta2 = 0;


	deltaY = pt_prev.y() - pt.y();
	deltaX = pt_prev.x() - pt.x();
	theta1 = atan2(deltaY, deltaX);
	if(theta1 < 0)
		theta1 += (D360);

	deltaY = pt_next.y() - pt.y();
	deltaX = pt_next.x() - pt.x();
	theta2 = atan2(deltaY, deltaX);
	if(theta2 < 0)
		theta2 += (D360);
	largerTheta = (theta1 >= theta2 ? theta1 : theta2);
	smallerTheta = (theta1 <= theta2 ? theta1 : theta2);
	deltaTheta = largerTheta - smallerTheta;
	if(deltaTheta > D180) {
		deltaTheta = D360 - deltaTheta;
		bisectorAngle = (largerTheta + smallerTheta)/2 + D180;
	}
	else
		bisectorAngle = (largerTheta + smallerTheta)/2;

	angle = deltaTheta;

	if(bisectorAngle > D360)
		bisectorAngle -= D360;

	return bisectorAngle;

}//End of getBisectorAngle


/**********************************************************************
 * writeImgWithAllDetails
 *
 * This function is used for testing purposes, to mark all the details on the image
 * storing it on the disk
 **********************************************************************/
void ARAB_IMAGE::writeImgWithAllDetails(const char* FNAME) {
	ARAB_IMAGE tmpImag;
	tmpImag.create(get_xsize(), get_ysize(), 8);
	copy_sub_image(this, 0, 0, get_xsize(), get_ysize(),&tmpImag, 0, 0, TRUE);
	tmpImag.makeThinnedImage(true);

	PixelWithFeatures_IT features_it = &featurePointsLst;
	features_it.move_to_first();

	for(features_it.mark_cycle_pt(); !features_it.cycled_list(); features_it.forward()) {
		if(!(features_it.data()->feature == CUT_PT)) {
			if(features_it.data()->feature == BRANCH_PT)
				tmpImag.putPixelThinned(features_it.data()->coords, 250);
			else
				tmpImag.putPixelThinned(features_it.data()->coords, 230);
		}
	}

	markSegPointsOnImage(tmpImag.getThinnedImage());
	tmpImag.getThinnedImage()->write(FNAME);
	


}//End of writeImgWithAllDetails

/**********************************************************************
 * removeSeconderies
 *
 * It is neede to remove the seconderes when filtering the segmetation points 
 * not to affect the detection of compex shapes based on the vertical extention of the
 * of the candidate segmentation point
 **********************************************************************/
void ARAB_IMAGE::removeSeconderies() {
	BYTE	WHITE = get_white_level();
	for(int i = 1; i <= numOfCmps_BeforeSeg; i++) {
		if(connCmpPtr_BeforeSeg[i].is_secondery) {
			for(int xt = connCmpPtr_BeforeSeg[i].x1; xt <= connCmpPtr_BeforeSeg[i].x2; xt++) {
				for(int yt = connCmpPtr_BeforeSeg[i].y1; yt <= connCmpPtr_BeforeSeg[i].y2; yt++) {
					if(pixel(xt, yt) == i) {
						putPixelImage(ICOORD(xt, yt), WHITE);
					}
				}
			}
		}
	}
}//End ofremoveSeconderies


/**********************************************************************
 * makeImagBinary
 *
 * For the route detection functions, the image should have only white and black pixles;
 * after calling extractSubwords, the components will be colored. This function removes the colors.
 **********************************************************************/
void ARAB_IMAGE::makeImagBinary() {
	BYTE WHITE = get_white_level();
	for(int xt = 0; xt < get_xsize(); xt++) {
		for(int yt = 0; yt < get_ysize(); yt++) {
			if(pixel(xt, yt) != WHITE) {
				putPixelImage(ICOORD(xt, yt), 0);
			}
		}
	}
}//End of makeImagBinary


/**********************************************************************
 * filterBadFeatures
 *
 * Deletes features that are not suitable to be used.
 * The only feature considered bad by this function is the branch going downward
 **********************************************************************/
void ARAB_IMAGE::filterBadFeatures(Route_IT* route_it) {//the current version of this function filters out bad branches
	BYTE N8[8];
	BYTE WHITE = get_white_level();
	for(route_it->mark_cycle_pt(); !route_it->cycled_list(); route_it->forward()) {
		if(route_it->data()->firstPt.feature == BRANCH_PT) {
			int dumx = route_it->data()->firstPt.coords.x();
			int dum = route_it->data()->lastPt.coords.x();
			getN8(route_it->data()->firstPt.coords, N8, thinned);
			if(N8[1] == WHITE && N8[2] == VISITED_PT && N8[3] == WHITE) {//Visited to thinnedGreyLevel
				route_it->data()->firstPt.feature = BAD_BRANCH_PT;
			}
		}
		if(route_it->data()->lastPt.feature == BRANCH_PT) {
			getN8(route_it->data()->firstPt.coords, N8, thinned);
			if(N8[1] == WHITE && N8[2] == VISITED_PT && N8[3] == WHITE) {
				route_it->data()->firstPt.feature = BAD_BRANCH_PT;
			}
		}
	}
}//End of filterBadFeatures


/**********************************************************************
 * copySegmentationMarks
 *
 * WRITE THIS BACK_TO
 **********************************************************************/
void ARAB_IMAGE::copySegmentationMarks(ARAB_IMAGE* source, IMAGE* dest) {
	
}//End of copySegmentationMarks


/**********************************************************************
 * colorConnectedComponents
 *
 * WRITE THIS BACK_TO
 **********************************************************************/
void ARAB_IMAGE::colorConnectedComponents() {
	//connCmpPtr_BeforeSeg = Connected_Components_OLD(this, numOfCmps_BeforeSeg, "DUMMY");
	connCmpPtr_BeforeSeg = Connected_Components(this, numOfCmps_BeforeSeg, "DUMMY");	
}//End of colorConnectedComponents

/**********************************************************************
 * identifySeconderies
 *
 * WRITE THIS BACK_TO
 **********************************************************************/
void ARAB_IMAGE::identifySeconderies() {
	Identify_Seconderies(this, numOfCmps_BeforeSeg, connCmpPtr_BeforeSeg);
}//End of identifySeconderies

/**********************************************************************
 * assignSeconderiesSubwords
 *
 * WRITE THIS BACK_TO
 **********************************************************************/
void ARAB_IMAGE::assignSeconderiesSubwords() {
	Assign_Seconderies(this, numOfCmps_BeforeSeg, connCmpPtr_BeforeSeg, inc_x1_codes, dec_x2_codes);
}//End of assignSeconderiesSubwords

/**********************************************************************
 * extractSubwords
 *
 * WRITE THIS BACK_TO
 **********************************************************************/
void ARAB_IMAGE::extractSubwords() {
	subwordsPtr =  Extract_Subwords(this,
									numOfCmps_BeforeSeg,
									connCmpPtr_BeforeSeg,
									inc_x1_codes,
									dec_x2_codes,
									numOfSubwords);
}//End of extractSubwords

/**********************************************************************
 * segmentToChars
 *
 * WRITE THIS BACK_TO
 **********************************************************************/
void ARAB_IMAGE::segmentToChars(IMAGE* dest) {
	ARAB_IMAGE		tmpImg;
	int				lowest_pt = 19;	
	BYTE			WHITE = get_white_level();
	IMAGELINE		empty_line;

	findBaseline();
	colorConnectedComponents();
	identifySeconderies();
	assignSeconderiesSubwords();
	extractSubwords();


	tmpImg.create(get_xsize(), get_ysize(), 8);
	tmpImg = this;
	tmpImg.removeSeconderies();
	tmpImg.makeImagBinary();
	tmpImg.makeThinnedImage(false);
	tmpImg.findRoutes();
	tmpImg.detectLoops();//SHOULD BE CALLED TO AVOID INFINITE LOOPS IN ARAB_IMAGE::findEdgeFeatures()
	tmpImg.findSubtleBranchFeatures();
	tmpImg.findEdgeFeatures();
	tmpImg.findSegmentationPoints(this);

	tmpImg.markSegPointsOnImage(this);
	//write("test4.tif");
	makeImagBinary();

	connCmpPtr_AfterSeg = Connected_Components(this, numOfCmps_AfterSeg, "DUMMY");

	for (int i = 1; i <= numOfCmps_BeforeSeg; i++) {
		if ( connCmpPtr_BeforeSeg[i].is_secondery ) {
			for (int j = 1; j <= numOfCmps_AfterSeg; j++) {
				if (connCmpPtr_AfterSeg[j].x1 == connCmpPtr_BeforeSeg[i].x1 &&
					connCmpPtr_AfterSeg[j].x2 == connCmpPtr_BeforeSeg[i].x2 &&
					connCmpPtr_AfterSeg[j].y1 == connCmpPtr_BeforeSeg[i].y1 &&
					connCmpPtr_AfterSeg[j].y2 == connCmpPtr_BeforeSeg[i].y2)
				{
					connCmpPtr_AfterSeg[j].is_secondery = true;
					connCmpPtr_AfterSeg[j].groupSecondery = connCmpPtr_BeforeSeg[i].groupSecondery;
					break;
				}
			}
		}
	}
	assignSeconderiesLetters();
	extractLetters();
	//write("test3.tif");

	empty_line.init();
	for( int s = 0; s < get_xsize(); s++ )
		empty_line.pixels[s] = WHITE;
	for( int s = 0; s < get_ysize(); s++ )
		tmpImg.put_line(0, s, page_image.get_xsize(), &empty_line, 0);//clear image
#ifdef	WRITE_IMAGE_WITH_BOXES
	IMAGE		imgCharsWithBorder;
	IMAGELINE	tmpLine;

	tmpLine.init();
	imgCharsWithBorder.create(get_xsize(), get_ysize(), 1);
	invert_image(&imgCharsWithBorder);
	for( int s = 0; s < get_xsize(); s++ )
		tmpLine.pixels[s] = !WHITE;
	for( int s = 1; s <= numOfCharacters; s++ ) {
		copy_sub_image(this, 
			charactersPtr[s].x1,
			charactersPtr[s].y1, 
			charactersPtr[s].x2 - charactersPtr[s].x1 + 1, 
			charactersPtr[s].y2 - charactersPtr[s].y1 + 1,
			&imgCharsWithBorder, 
			charactersPtr[s].x1, charactersPtr[s].y1, 
			TRUE);
		imgCharsWithBorder.put_column(charactersPtr[s].x1 - 3,
			charactersPtr[s].y1 - 3,
			charactersPtr[s].y2 - charactersPtr[s].y1 + 1 + 6,
			&tmpLine,
			0);
		imgCharsWithBorder.put_column(charactersPtr[s].x2 + 3,
			charactersPtr[s].y1 - 3,
			charactersPtr[s].y2 - charactersPtr[s].y1 + 1 + 6,
			&tmpLine,
			0);
		imgCharsWithBorder.put_line(charactersPtr[s].x1 - 3,
			charactersPtr[s].y1 - 3,
			charactersPtr[s].x2 - charactersPtr[s].x1 + 1 + 6,
			&tmpLine,
			0);
		imgCharsWithBorder.put_line(charactersPtr[s].x1 - 3,
			charactersPtr[s].y2 + 3,
			charactersPtr[s].x2 - charactersPtr[s].x1 + 1 + 6,
			&tmpLine,
			0);
	}

	const char* fileName = strrchr(__argv[1], '\\');
	fileName++;

	char* FINAL_FJ = new char[50];
	strcpy(FINAL_FJ, "");
	strcat(FINAL_FJ, "FINAL_WITH_BORDER_");
	strcat(FINAL_FJ, fileName);
	imgCharsWithBorder.write(FINAL_FJ);
#endif

	//this->write("test.tif");
	for( int s = 1; s <= numOfCharacters; s++ ) {
		if (connCmpPtr_AfterSeg[s].y1 > lowest_pt) {
			copy_sub_image(this, 
							charactersPtr[s].x1,
							charactersPtr[s].y1, 
							charactersPtr[s].x2 - charactersPtr[s].x1 + 1, 
							charactersPtr[s].y2 - charactersPtr[s].y1 + 1,
							&tmpImg, 
							charactersPtr[s].x1, charactersPtr[s].y1,//lowest_pt+1,//FUAD:: Sep 26, 2009 test 
							TRUE);
		}
	}

	dest->destroy();
	dest->create(get_xsize(), get_ysize(), 1);
	copy_sub_image(&tmpImg, 0, 0, get_xsize(), get_ysize(), dest, 0, 0, TRUE);
}//End of segmentToChars

/**********************************************************************
 * assignSeconderiesLetters
 *
 * WRITE THIS BACK_TO
 **********************************************************************/
void ARAB_IMAGE::assignSeconderiesLetters() {
	Assign_Seconderies_Letters(this, 
								numOfCmps_AfterSeg, 
								connCmpPtr_AfterSeg, 
								inc_x1_codes_Segmented, 
								dec_x2_codes_Segmented);
}//End of assignSeconderiesLetters

/**********************************************************************
 * extractLetters
 *
 * WRITE THIS BACK_TO
 **********************************************************************/
void ARAB_IMAGE::extractLetters() {
	charactersPtr = Extract_Letters(this,
									numOfCmps_AfterSeg, 
									connCmpPtr_AfterSeg, 
									inc_x1_codes_Segmented, 
									dec_x2_codes_Segmented, 
									numOfCharacters);
	/*numOfCmps_Final = numOfCmps_AfterSeg;
	numOfChars_Final = numOfCharacters;
	for(int i = 1; i < numOfCmps_AfterSeg; i++) {//Filtering noise
		if(connCmpPtr_AfterSeg[i].pixelsCnt < 20 && connCmpPtr_AfterSeg[i].is_secondery == false) {
			for(int xt = connCmpPtr_AfterSeg[i].x1; xt <= connCmpPtr_AfterSeg[i].x2; xt++)
				for(int yt = connCmpPtr_AfterSeg[i].y1; yt <= connCmpPtr_AfterSeg[i].y2; yt++)
					putPixelImage(ICOORD(xt, yt), get_white_level());

			numOfChars_Final--;
			numOfCmps_Final--;
		}
	}*/
}//End of extractLetters


/**********************************************************************
 * operator=
 *
 * WRITE THIS BACK_TO
 **********************************************************************/
void ARAB_IMAGE::operator=(ARAB_IMAGE* other) {
	copy_sub_image(other, 0, 0, get_xsize(), get_ysize(), this, 0, 0, TRUE);
	//this->write("test.tif");
	connCmpPtr_BeforeSeg = new connCmpInf[other->numOfCmps_BeforeSeg + 1];
	subwordsPtr = new subword[other->numOfSubwords + 1];
	numOfCmps_BeforeSeg = other->numOfCmps_BeforeSeg;
	numOfSubwords = other->numOfSubwords;
	BaseLine = other->BaseLine;

	for(int i = 1; i <= numOfCmps_BeforeSeg; i++) {
		connCmpPtr_BeforeSeg[i].copy(&(other->connCmpPtr_BeforeSeg[i]));
	}
	for(int i = 1; i <= numOfSubwords; i++) {
		subwordsPtr[i] = other->subwordsPtr[i];
	}
}//End of operator=



/**********************************************************************
 * preRecognizeChars
 *
 * WRITE THIS BACK_TO
 **********************************************************************/
void ARAB_IMAGE::preRecognizeChars(connCmpInf* connCmpPtr, int noOfCmps){
	PixelWithFeatures_IT	features_it;
	Route_IT				route_it;
	int						endPt_cnt = 0;
	int						brnPt_cnt = 0;
	int						subtleBrn_cnt = 0;
	int						edgePt_cnt = 0;
	PixelWithFeatures*		endPt1;
	PixelWithFeatures*		endPt2;
	PixelWithFeatures*		endPt3;
	PixelWithFeatures*		brnPt1;
	PixelWithFeatures*		subtleBrnPt1;
	PixelWithFeatures*		edgePt1;
	PixelWithFeatures*		edgePt2;
	PixelWithFeatures*		edgePt3;
	PixelWithFeatures*		PT1;//left most feature point
	PixelWithFeatures*		PT2;//right most feature point
	STATS					heightAfterSeg(0, 200);
	STATS					widthAfterSeg(0, 200);
	double					slope = 0;
	bool					alef;
	bool					flag;
	bool					charRecognised = false;
	Route					*route1, *route2;
	ICOORD					route1_leftPt, route1_rightPt, route2_leftPt, route2_rightPt;
	



	for(int i = 1; i <= noOfCmps; i++) {
		heightAfterSeg.add(connCmpPtr[i].height, 1);
		widthAfterSeg.add(connCmpPtr[i].width, 1);
	}
	double	avgHeightAfterSeg = heightAfterSeg.mean();
	int		medianHeightAfterSeg = heightAfterSeg.median();
	double	avgWidthAfterSeg = widthAfterSeg.mean();
	int		medianWidthAfterSeg = widthAfterSeg.median();


	for(int i = 1; i <= noOfCmps; i++) {
		endPt_cnt = 0;
		brnPt_cnt = 0;
		subtleBrn_cnt = 0;
		edgePt_cnt = 0;
		endPt1 = NULL;
		endPt2 = NULL;
		endPt3 = NULL;
		brnPt1 = NULL;
		edgePt1 = NULL;
		edgePt2 = NULL;
		edgePt3 = NULL;
		subtleBrnPt1 = NULL;
		alef = false;
		charRecognised = false;
		features_it.set_to_list(&(connCmpPtr[i].featurePointsLst));
		route_it.set_to_list(&(connCmpPtr[i].routeList));
		int tmpNumOfFeats = features_it.length();
		int tmpX1 = connCmpPtr[i].x1;
		
		//Simple case: one shourt Route characeter -> PART_OF_CHAR
		if(route_it.length() == 1 && route_it.data()->coordsList.length() < 15) {
			connCmpPtr[i].arabChar = PART_OF_CHAR;
			charRecognised = true;
		}
		//LOOP
		if(!charRecognised) {
			for(route_it.mark_cycle_pt(); !route_it.cycled_list(); route_it.forward()) {
				if(route_it.data()->isPartOfLoop) {
					connCmpPtr[i].arabChar = LOOP;
					charRecognised = true;
					break;
				}
			}
			if(!charRecognised) {
				int tmpRouteCnt = route_it.length();
				for(route_it.mark_cycle_pt(); !route_it.cycled_list(); route_it.forward()) {
					route1 = route_it.data();
					route2 = route_it.data_relative(1);
					route1_leftPt = route1->firstPt.coords.x() < route1->lastPt.coords.x() ? route1->firstPt.coords : route1->lastPt.coords;
					route1_rightPt = route1->firstPt.coords.x() > route1->lastPt.coords.x() ? route1->firstPt.coords : route1->lastPt.coords;
					route2_leftPt = route2->firstPt.coords.x() < route2->lastPt.coords.x() ? route2->firstPt.coords : route2->lastPt.coords;
					route2_rightPt = route2->firstPt.coords.x() > route2->lastPt.coords.x() ? route2->firstPt.coords : route2->lastPt.coords;
					if(route1 != route2) {
						if(route1_leftPt.pt_to_pt_dist(route2_leftPt) < 5
						&& route1_rightPt.pt_to_pt_dist(route2_rightPt) < 5
						) {
							connCmpPtr[i].arabChar = LOOP;
							charRecognised = true;
							break;
						}
					}
				}
			}

		}
		////End of LOOP detection
		if(features_it.length() <= 6) {
			features_it.sort(featComparator);
			features_it.move_to_first();
			PT1 = features_it.data();
			PT2 = features_it.data_relative(-1);
			for(features_it.mark_cycle_pt(); !features_it.cycled_list(); features_it.forward()) {
				if(features_it.data()->feature == END_PT) {
					endPt_cnt++;
					switch(endPt_cnt) {
						case 1: endPt1 = features_it.data(); break;
						case 2: endPt2 = features_it.data(); break;
						case 3: endPt3 = features_it.data(); break;
					}
				}
				if(features_it.data()->feature == BRANCH_PT) {
					brnPt_cnt++;
					switch(brnPt_cnt) {
						case 1: brnPt1 = features_it.data(); break;
					}
				}
				if(features_it.data()->feature == SUBTLE_BRANCH_PT) {
					subtleBrn_cnt++;
					switch(subtleBrn_cnt) {
						case 1: subtleBrnPt1 = features_it.data(); break;
					}
				}
				if(features_it.data()->feature == EDGE_PT) {
					edgePt_cnt++;
					switch(edgePt_cnt) {
						case 1: edgePt1 = features_it.data(); break;
						case 2: edgePt2 = features_it.data(); break;
						case 3: edgePt3 = features_it.data(); break;
					}
				}
			}
			//PART_OF_MEEM
			if(!charRecognised && endPt_cnt == 2 && brnPt_cnt == 0 && edgePt_cnt <= 2 && edgePt_cnt > 0) {
				if((edgePt1 != NULL ? isAnglesEqual(edgePt1->angle, D315, D45) : true)
				&& (edgePt2 != NULL ? isAnglesEqual(edgePt2->angle, D315, D45) : true)
				) {
					connCmpPtr[i].arabChar = PART_OF_MEEM;
					charRecognised = true;
				}
			}
			////End of PART_OF_MEEM
			//SIN_M
			if(endPt_cnt == 3 && brnPt_cnt == 1 && edgePt_cnt == 0 && !charRecognised) {
				if(brnPt1->coords.x() > endPt1->coords.x() && brnPt1->coords.x() < endPt3->coords.x()) {
					connCmpPtr[i].arabChar = SIN_M;
					charRecognised = true;
				}
			}
			if(endPt_cnt == 2 && subtleBrn_cnt == 1 && !charRecognised) {
				if(subtleBrnPt1->coords.x() > endPt1->coords.x() && subtleBrnPt1->coords.x() < endPt2->coords.x()) {
					connCmpPtr[i].arabChar = SIN_M;
					charRecognised = true;
				}
			}
			////
			//SIN_B
			if(endPt_cnt == 2 && edgePt_cnt <= 3 && brnPt_cnt == 0 && !charRecognised) {
				if((edgePt1 != NULL ? isAnglesEqual(edgePt1->angle, D180, D90) : true)
				&& (edgePt2 != NULL ? isAnglesEqual(edgePt2->angle, D180, D90) : true)
				&& (edgePt3 != NULL ? isAnglesEqual(edgePt3->angle, D180, D90) : true)
				//|| (edgePt_cnt == 0)
				) {
					if(endPt2->coords.x() - endPt1->coords.x() != 0) {
						slope = (double)(endPt2->coords.y() - endPt1->coords.y())/(endPt2->coords.x() - endPt1->coords.x());
					} else {
						slope = 1000;//vertical line
					}
					if(slope > 0.4) {
						connCmpPtr[i].arabChar = SIN_M;//Tmp SIN_M, make it SIN_B
						charRecognised = true;
					}
				}
			}
			////
			//RAA2_E
			if(endPt_cnt <= 3 && brnPt_cnt <= 1 && edgePt_cnt <= 2 && !charRecognised) {
				if(endPt_cnt == 2 && edgePt_cnt == 2 && brnPt_cnt == 0) {
					if((isAnglesEqual(edgePt2->angle, D315, D90))
					&& (isAnglesEqual(edgePt1->angle, D135, D90))) {
						connCmpPtr[i].arabChar = RAA2_E;
						charRecognised = true;
					}
				}
				if(endPt_cnt == 3 && brnPt_cnt == 1 && edgePt_cnt == 1) {
					if (isAnglesEqual(edgePt1->angle, D135, D90)) {
						connCmpPtr[i].arabChar = RAA2_E;
						charRecognised = true;
					}
				}
			}
			////
			//CURVE_E
			if(endPt_cnt == 2 && edgePt_cnt >= 1 && edgePt_cnt <= 3 && !charRecognised) {
				if((edgePt1 != NULL ? isAnglesEqual(edgePt1->angle, 0, D90) : true)
				&& (edgePt2 != NULL ? isAnglesEqual(edgePt2->angle, 0, D90) : true)
				&& (edgePt3 != NULL ? isAnglesEqual(edgePt3->angle, 0, D90) : true)) {
					if(edgePt_cnt == 1) {
						/*if(!(abs(route_it.data()->coordsList.length() - route_it.data_relative(1)->coordsList.length()) < 7)) {
							for(route_it.mark_cycle_pt(); !route_it.cycled_list(); route_it.forward()) {
								if(abs(route_it.data()->lastPt.coords.y() - route_it.data()->firstPt.coords.y()) < route_it.data()->coordsList.length()/2) {
									alef = true;
									break;
								}
							}
						}*/
						if((double)connCmpPtr[i].height / connCmpPtr[i].width > 3.5) {
							alef = true;
						}
						//another check for alef, if the char contains two routes and one of them is nearly vertical
						//then the character is most likely an alef
						if(route_it.length() == 2) {
							for(route_it.mark_cycle_pt(); !route_it.cycled_list(); route_it.forward()) {
								if(route_it.data()->firstPt.coords.x() - route_it.data()->lastPt.coords.x() < 5) {
									alef = true;
								}
							}
						}

						if(!alef) {
							connCmpPtr[i].arabChar = CURVE_E;
							charRecognised = true;
						}
					} else {
						connCmpPtr[i].arabChar = CURVE_E;
						charRecognised = true;
					}
				}
			}
			////
			//PART_OF_CHAR
			if(((connCmpPtr[i].width < (avgWidthAfterSeg/2) && connCmpPtr[i].width < (medianWidthAfterSeg/2))
			&& (connCmpPtr[i].height < (avgHeightAfterSeg/2) && connCmpPtr[i].height < (medianHeightAfterSeg/2)))
			|| connCmpPtr[i].pixelsCnt < 25  
			&& !charRecognised
			) {
				connCmpPtr[i].arabChar = PART_OF_CHAR;
				charRecognised = true;
			}
			////
			//Check for horizontal segments that are not seen in the beginning of a subword
			/*if(!(endPt_cnt == 2 && edgePt_cnt == 0 && brnPt_cnt == 1 && connCmpPtr[i].width > 40) && !charRecognised
				&& ((double)connCmpPtr[i].width / connCmpPtr[i].height > 2.5) ) {//Min seen horizontal line length
				if(PT2->coords.x() - PT1->coords.x() != 0) {
					slope = (double)(PT2->coords.y() - PT1->coords.y())/(PT2->coords.x() - PT1->coords.x());
				} else {
					slope = 1000;//vertical line
				}
				flag = false;
				if(slope <= 1) {
					flag = true;
					for(features_it.mark_cycle_pt(); !features_it.cycled_list(); features_it.forward()) {
						if(findDistLinePt(PT1->coords, PT2->coords, features_it.data()->coords) > 5) {
							flag = false;
							break;
						}
					}
				}
				if(flag) {
					connCmpPtr[i].arabChar = PART_OF_CHAR;
					charRecognised = true;
				}
			}*/
			
		}
	}
}//End of preRecognizeChars

/**********************************************************************
 * findSegPointsInComponent
 *
 * WRITE THIS BACK_TO
 **********************************************************************/
void ARAB_IMAGE::findSegPointsInComponent(BYTE cmpCode, connCmpInf* connCmpPtr, SEG_POINT_IT* segPts_it) {
	Route_IT			route_it = &routeList;
	PixelWithDiffs_IT	pxDers_it;
	PixelWithFeatures	PT1;			//left end of the current Route
	PixelWithFeatures	PT2;			//right end of the current Route
	PixelWithFeatures	PT1_prev;		//point	information of the Route to the left of the current route
	PixelWithFeatures	PT2_prev;		
	PixelWithFeatures	PT1_next;		//point information if the Route to the right of the current route
	PixelWithFeatures	PT2_next;
	ICOORD				cutPos;
	//Route_LIST			vertRoutes;
	//Route_IT			vertRoutes_it;

	bool				routeGotCutPt = false;
	double				slope = 0;

	double				MAX_SLOPE = 0.7;
	int					MIN_LENGTH_TO_CONSIDER = 10;
	int					LONG_ROUTE = 30;

	int					subWordCnt = 0;
	bool				validEndPoint = false;

	bool				gotBodyAbove = false;
	bool				gotBodyBelow = false;
	BYTE				WHITE = get_white_level();
	int					xt = 0, yt = 0;
	int					maxY = get_ysize();

	int					cmpWidth = connCmpPtr[cmpCode].width;
	int					cmpHeight = connCmpPtr[cmpCode].height;
	int					cmpArea = connCmpPtr[cmpCode].area;
	int					cmpPixCnt = connCmpPtr[cmpCode].pixelsCnt;
	int					cmpNumOfRoutes = connCmpPtr[cmpCode].routeList.length();
	int					cmpNumOfFeatures = connCmpPtr[cmpCode].featurePointsLst.length();

	//copyRoutesList(&(connCmpPtr[cmpCode].routeList), &vertRoutes);
	//vertRoutes_it.set_to_list(&vertRoutes);

#ifdef MAX_ROUTE_SLOPE_TO_SEGMENT
	MAX_SLOPE = MAX_ROUTE_SLOPE_TO_SEGMENT;
#endif

#ifdef MIN_ROUTE_LENGTH_TO_SEGMENT
	MIN_LENGTH_TO_CONSIDER = MIN_ROUTE_LENGTH_TO_SEGMENT;
#endif

	route_it.set_to_list(&(connCmpPtr[cmpCode].routeList));
	route_it.sort(routeComparator);
	route_it.move_to_first();
	for(route_it.mark_cycle_pt(); !route_it.cycled_list(); route_it.forward()) {
		if(!route_it.data()->isPartOfLoop) {
			routeGotCutPt = false;
			int rtXX = route_it.data()->firstPt.coords.x();
			pxDers_it.set_to_list(route_it.data()->getPixelWithDiffsList());
			if(pxDers_it.length() > MIN_LENGTH_TO_CONSIDER) {
				//prev and next points may be used to put constraints on the segmentation points according
				//to the properties of the neighboring routes
				PT1_prev = route_it.data_relative(-1)->firstPt.coords.x() < route_it.data_relative(-1)->lastPt.coords.x() ? route_it.data_relative(-1)->firstPt : route_it.data_relative(-1)->lastPt;//left
				PT2_prev = route_it.data_relative(-1)->firstPt.coords.x() >= route_it.data_relative(-1)->lastPt.coords.x() ? route_it.data_relative(-1)->firstPt : route_it.data_relative(-1)->lastPt;//right
				PT1_next = route_it.data_relative(1)->firstPt.coords.x() < route_it.data_relative(1)->lastPt.coords.x() ? route_it.data_relative(1)->firstPt : route_it.data_relative(1)->lastPt;//left
				PT2_next = route_it.data_relative(1)->firstPt.coords.x() >= route_it.data_relative(1)->lastPt.coords.x() ? route_it.data_relative(1)->firstPt : route_it.data_relative(1)->lastPt;//right
				PT1 = route_it.data()->firstPt.coords.x() < route_it.data()->lastPt.coords.x() ? route_it.data()->firstPt : route_it.data()->lastPt;//left
				PT2 = route_it.data()->firstPt.coords.x() >= route_it.data()->lastPt.coords.x() ? route_it.data()->firstPt : route_it.data()->lastPt;//right

				if(!(PT1.coords == PT1_prev.coords || PT1.coords == PT2_prev.coords || PT2.coords == PT1_prev.coords || PT2.coords == PT2_prev.coords)) {
					PT1_prev = PT1;
					PT2_prev = PT2;
				}
				if(!(PT1.coords == PT1_next.coords || PT1.coords == PT2_next.coords || PT2.coords == PT1_next.coords || PT2.coords == PT2_next.coords)) {
					PT1_prev = PT1;
					PT2_prev = PT2;
				}

				if(abs(double(PT1.coords.x() - PT2.coords.x())) == 0.0) {
					slope = 100;//if the Route is vertical use a slope larger than MAX_SLOPE to ignore this Route
				} else {
					slope = abs(double(PT1.coords.y() - PT2.coords.y()))/abs(double(PT1.coords.x() - PT2.coords.x()));
				}

				if(PT2.feature == EDGE_PT) {	//starting with PT2 feature
					if(PT1.feature == EDGE_PT) {
						//further constraints are put below; the function isAnglesEqual(angle1, angle2, tolerance) returns ture if 
						//angle1 is within +or- tolerance from angle2. ALL IN RADIANS
						//e.g. angle1 = 2.443(140 degree) angle2 = D135(defined at the beginning of the file)
						//tolerance = .35(20 degree) the function will return true
						if((isAnglesEqual(PT2.angle, 125, 1.4)) 
						&& (isAnglesEqual(PT1.angle, 135, 2) || isAnglesEqual(PT1.angle, D315, 30))
						&& (route_it.data()->coordsList.length() > 15)) {
							if(slope < MAX_SLOPE) {
								routeGotCutPt = true;
							}	
						}
					}
					if(PT1.feature == CROSS_PT || PT1.feature == BRANCH_PT || PT1.feature == SUBTLE_BRANCH_PT) {
						if(isAnglesEqual(PT2.angle, D90, D90) || isAnglesEqual(PT2.angle, D180, D45))
							if(slope < MAX_SLOPE)
								routeGotCutPt = true;
					}
				}
				if(PT2.feature == BRANCH_PT || PT2.feature == CROSS_PT || PT2.feature == SUBTLE_BRANCH_PT){
					if(PT1.feature == EDGE_PT) {
						if(isAnglesEqual(PT1.angle, D45, 0.55) || isAnglesEqual(PT1.angle, D225, 0.35)) {//TEST D225
							if(slope < MAX_SLOPE)
								routeGotCutPt = true;
						}

						if(isAnglesEqual(PT1.angle, D315, 0.5) || isAnglesEqual(PT1.angle, D00, 0.35)) {
							if(PT2.feature != SUBTLE_BRANCH_PT)
								if(slope < MAX_SLOPE)
									routeGotCutPt = true;
						}
					}
					if(PT1.feature == BRANCH_PT || PT1.feature == CROSS_PT || PT1.feature == SUBTLE_BRANCH_PT) {
						if(slope < MAX_SLOPE) {
							if(pxDers_it.length() > MIN_ROUTE_LENGTH_BR_BR) {
								routeGotCutPt = true;
							}
						}
					}
				}

				if(PT2.feature == END_PT) {
					validEndPoint = (abs(connCmpPtr[cmpCode].x2 - PT2.coords.x()) < MIN_ROUTE_LENGTH_TO_SEGMENT);
					if(validEndPoint) {
						if(PT1.feature == BRANCH_PT || PT1.feature == SUBTLE_BRANCH_PT) {
							if(slope < 1.75)
								routeGotCutPt = true;
						}
						if(PT1.feature == EDGE_PT) {
							if(isAnglesEqual(PT1.angle, D45, 0.45) || isAnglesEqual(PT1.angle, D315, 0.45)) {
								if(slope < 2.5)
									routeGotCutPt = true;
							}
						}
					}
				}

				if(PT2.feature == EDGE_PT && isAnglesEqual(PT2.angle, D90, 0.35)) {
					if(PT1.feature != END_PT) {
						routeGotCutPt = true;
					}
				}
				//adding extra rules for routes that are ruined by an edge in the middle
				/*if((PT2_next.feature == EDGE_PT && isAnglesEqual(PT2_next.angle, D135, D45))
				|| (PT2_next.feature == BRANCH_PT || PT2_next.feature == SUBTLE_BRANCH_PT)
				) {
					if((PT1.feature == BRANCH_PT || PT1.feature == SUBTLE_BRANCH_PT) && isAnglesEqual(PT2.angle, 0, 35*D180/180)) {
						routeGotCutPt = true;
					}
				}*/
				if(routeGotCutPt) {
					//the function findCutPosition returns true always(for now) and detects the segmentation point as the middle 
					//point of the Route. I'll modify it so that it becomes a filter stage; returns false if the 
					//detected segmentation point is far from the baseline or the segmentation point passes a loop
					//This filtering stage adds flexibility in choosing the segmentation rules
					if(findCutPosition(route_it.data(), &cutPos)) {
						segPts_it->add_after_then_move(new SEG_POINT(cutPos));
					}
				}
			}
		}
	}
}//End of findSegPointsInComponent


void ARAB_IMAGE::copyRoutesList(Route_LIST* source, Route_LIST* dest) {
	Route_IT			route_it = source;
	Route_IT			destRoute_it = dest;
	PixelWithDiffs_IT*	pxDers_it = new PixelWithDiffs_IT();	
	route_it.move_to_first();
	dest->clear();
	for(route_it.mark_cycle_pt(); !route_it.cycled_list(); route_it.forward()) {
		Route* tmpRoute = new Route();
		PixelWithDiffs_IT destPxDers_it = &(tmpRoute->coordsList);
		pxDers_it->set_to_list(&(route_it.data()->coordsList));
		pxDers_it->move_to_first();
		for(pxDers_it->mark_cycle_pt(); !pxDers_it->cycled_list(); pxDers_it->forward()) {
			destPxDers_it.add_after_then_move(new PixelWithDiffs(pxDers_it->data()->coords));//derivatives not copied
		}
		tmpRoute->firstPt = route_it.data()->firstPt;
		tmpRoute->lastPt = route_it.data()->lastPt;
		tmpRoute->isInLoopDetection = route_it.data()->isInLoopDetection;
		tmpRoute->isPartOfLoop = route_it.data()->isPartOfLoop;
		destRoute_it.add_after_then_move(tmpRoute);
	}
}

void ARAB_IMAGE::copyFeaturesList(PixelWithFeatures_LIST* source, PixelWithFeatures_LIST* dest) {
	PixelWithFeatures_IT	source_it = source;
	PixelWithFeatures_IT	dest_it = dest;

	source_it.move_to_first();
	dest->clear();

	for(source_it.mark_cycle_pt(); !source_it.cycled_list(); source_it.forward()) {
		PixelWithFeatures* tmpPixWFeats = new PixelWithFeatures();

		tmpPixWFeats->coords = source_it.data()->coords;
		tmpPixWFeats->feature = source_it.data()->feature;
		tmpPixWFeats->angle = source_it.data()->angle;
		dest_it.add_after_then_move(tmpPixWFeats);
	}
}

bool ARAB_IMAGE::cmpGotSegPoint(BYTE cmpCode) {

	return true;
}


bool ARAB_IMAGE::gotSeconderyBelow(connCmpInf* connCmpPtr, int noOfCmps, int x1, int x2, int ymin) {
	for(int i = 1; i <= noOfCmps; i++ ) {
		if(connCmpPtr[i].is_secondery) {
			if(connCmpPtr[i].x1 >= x1 && connCmpPtr[i].x2 <= x2 && connCmpPtr[i].y2 < ymin) {
				return true;
			}
		}
	}
	return false;
}